home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Extensions / Imaging / PIL / EpsImagePlugin.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  6.8 KB  |  313 lines

  1. #
  2. # The Python Imaging Library.
  3. # $Id: EpsImagePlugin.py,v 1.1.1.2 1999/01/13 09:39:47 sjoerd Exp $
  4. #
  5. # EPS file handling
  6. #
  7. # History:
  8. #    95-09-01 fl    Created
  9. #    96-05-18 fl    Don't choke on "atend" fields, Ghostscript interface
  10. #    96-08-22 fl    Don't choke on floating point BoundingBox values
  11. #    96-08-23 fl    Handle files from Macintosh
  12. #
  13. # Copyright (c) Secret Labs AB 1997.
  14. # Copyright (c) Fredrik Lundh 1995-96.
  15. #
  16. # See the README file for information on usage and redistribution.
  17. #
  18.  
  19.  
  20. __version__ = "0.3"
  21.  
  22.  
  23. import regex, string
  24. import Image, ImageFile
  25.  
  26. #
  27. # --------------------------------------------------------------------
  28.  
  29. def i32(c):
  30.     return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24)
  31.  
  32. def o32(i):
  33.     return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255)
  34.  
  35. split = regex.compile("^%%\([^:]*\):[ \t]*\(.*\)[ \t]*$")
  36. field = regex.compile("^%[%!]\([^:]*\)[ \t]*$")
  37.  
  38. def Ghostscript(tile, size, fp):
  39.     """Render an image using Ghostscript (Unix only)"""
  40.  
  41.     # Unpack decoder tile
  42.     decoder, tile, offset, data = tile[0]
  43.     length, bbox = data
  44.  
  45.     import tempfile, os
  46.  
  47.     file = tempfile.mktemp()
  48.  
  49.     # Build ghostscript command
  50.     command = ["gs",
  51.            "-q",            # quite mode
  52.            "-g%dx%d" % size,    # set output geometry (pixels)
  53.            "-dNOPAUSE -dSAFER",    # don't pause between pages, safe mode
  54.            "-sDEVICE=ppmraw",    # ppm driver
  55.            "-sOutputFile=%s" % file,# output file
  56.            "- >/dev/tty 2>/dev/tty"]
  57.  
  58.     command = string.join(command)
  59.  
  60.     # push data through ghostscript
  61.     try:
  62.     gs = os.popen(command, "w")
  63.     # adjust for image origin
  64.     if bbox[0] != 0 or bbox[1] != 0:
  65.         gs.write("%d %d translate\n" % (-bbox[0], -bbox[1]))
  66.     fp.seek(offset)
  67.     while length > 0:
  68.         s = fp.read(8192)
  69.         if not s:
  70.         break
  71.         length = length - len(s)
  72.         gs.write(s)
  73.     gs.close()
  74.     im = Image.core.open_ppm(file)
  75.     finally:
  76.     try: os.unlink(file)
  77.     except: pass
  78.  
  79.     return im
  80.  
  81.  
  82. class PSFile:
  83.     """Wrapper that treats either CR or LF as end of line."""
  84.     def __init__(self, fp):
  85.     self.fp = fp
  86.     def __getattr__(self, id):
  87.     v = getattr(self.fp, id)
  88.     setattr(self, id, v)
  89.     return v
  90.     def readline(self):
  91.     s = ""
  92.     c = self.fp.read(1)
  93.     while c not in "\r\n":
  94.         s = s + c
  95.         c = self.fp.read(1)
  96.     return s + "\n"
  97.  
  98.  
  99. def _accept(prefix):
  100.     return prefix[:4] == "%!PS" or i32(prefix) == 0xC6D3D0C5
  101.  
  102.  
  103. class EpsImageFile(ImageFile.ImageFile):
  104.     """EPS File Parser for the Python Imaging Library"""
  105.  
  106.     format = "EPS"
  107.     format_description = "Encapsulated Postscript"
  108.  
  109.     def _open(self):
  110.  
  111.     # FIXME: should check the first 512 bytes to see if this
  112.     # really is necessary (platform-dependent, though...)
  113.  
  114.     fp = PSFile(self.fp)
  115.  
  116.     # HEAD
  117.     s = fp.read(512)
  118.     if s[:4] == "%!PS":
  119.         offset = 0
  120.         fp.seek(0, 2)
  121.         length = fp.tell()
  122.     elif i32(s) == 0xC6D3D0C5: 
  123.         offset = i32(s[4:])
  124.         length = i32(s[8:])
  125.     else:
  126.         raise SyntaxError, "not an EPS file"
  127.  
  128.     fp.seek(offset)
  129.  
  130.     box = None
  131.  
  132.     self.mode = "RGB"
  133.     self.size = 1, 1 # FIXME: huh?
  134.  
  135.  
  136.     #
  137.     # Load EPS header
  138.  
  139.     s = fp.readline()
  140.  
  141.     while s:
  142.  
  143.         if len(s) > 255:
  144.         raise SyntaxError, "not an EPS file"
  145.  
  146.         if s[-2:] == '\r\n':
  147.         s = s[:-2]
  148.         elif s[-1:] == '\n':
  149.         s = s[:-1]
  150.  
  151.         try:
  152.         hit = split.match(s)
  153.         except regex.error, v:
  154.         raise SyntaxError, "not an EPS file"
  155.  
  156.         if hit >= 0:
  157.         k, v = split.group(1, 2)
  158.         self.info[k] = v
  159.         if k == "BoundingBox":
  160.             try:
  161.             # Note: The DSC spec says that BoundingBox
  162.             # fields should be integers, but some drivers
  163.             # put floating point values there anyway.
  164.             box = map(int, map(string.atof, string.split(v)))
  165.             self.size = box[2] - box[0], box[3] - box[1]
  166.             self.tile = [("eps", (0,0) + self.size, offset,
  167.                       (length, box))]
  168.             except:
  169.             pass
  170.  
  171.         elif field.match(s) >= 0:
  172.         k = field.group(1)
  173.         if k == "EndComments":
  174.             break
  175.         if k[:8] == "PS-Adobe":
  176.             self.info[k[:8]] = k[9:]
  177.         else:
  178.             self.info[k] = ""
  179.  
  180.         else:
  181.         raise IOError, "bad EPS header"
  182.  
  183.         s = fp.readline()
  184.         if s[:2] != "%%":
  185.         break
  186.  
  187.     #
  188.     # Scan for an "ImageData" descriptor
  189.  
  190.     while s[0] == "%":
  191.  
  192.         if len(s) > 255:
  193.         raise SyntaxError, "not an EPS file"
  194.  
  195.         if s[-2:] == '\r\n':
  196.         s = s[:-2]
  197.         elif s[-1:] == '\n':
  198.         s = s[:-1]
  199.  
  200.         if s[:11] == "%ImageData:":
  201.  
  202.         s = map(string.atoi, string.split(s[11:]))
  203.         [x, y, bi, mo, z3, z4, en, id] = s
  204.  
  205.         if en == 1:
  206.             decoder = "eps_binary"
  207.         elif en == 2:
  208.             decoder = "eps_hex"
  209.         else:
  210.             break
  211.         if bi != 8:
  212.             break
  213.         if mo == 1:
  214.             self.mode = "L"
  215.         elif mo == 2:
  216.             self.mode = "LAB"
  217.         elif mo == 3:
  218.             self.mode = "RGB"
  219.         else:
  220.             break
  221.  
  222.         # Scan forward to the actual image data
  223.         while 1:
  224.             s = fp.readline()
  225.             if not s:
  226.             break
  227.             if s[:len(id)] == id:
  228.             self.size = x, y
  229.             self.tile2 = [(decoder,
  230.                        (0, 0, x, y),
  231.                        fp.tell(),
  232.                        0)]
  233.             return
  234.  
  235.         s = fp.readline()
  236.         if not s:
  237.         break
  238.  
  239.     if not box:
  240.         raise IOError, "cannot determine EPS bounding box"
  241.  
  242.     def load(self, modify=0):
  243.     # Load EPS via Ghostscript
  244.     if not self.tile:
  245.         return
  246.     self.im = Ghostscript(self.tile, self.size, self.fp)
  247.     self.mode = self.im.mode
  248.     self.size = self.im.size
  249.     self.tile = []
  250.  
  251. #
  252. # --------------------------------------------------------------------
  253.  
  254. def _save(im, fp, filename, eps=1):
  255.     """EPS Writer for the Python Imaging Library.""" 
  256.  
  257.     #
  258.     # make sure image data is available
  259.     im.load()
  260.  
  261.     #
  262.     # determine postscript image mode
  263.     if im.mode == "L":
  264.     operator = (8, 1, "image")
  265.     elif im.mode == "RGB":
  266.     operator = (8, 3, "false 3 colorimage")
  267.     elif im.mode == "CMYK":
  268.     operator = (8, 4, "false 4 colorimage")
  269.     else:
  270.     raise ValueError, "image mode is not supported"
  271.  
  272.     if eps:
  273.     #
  274.     # write EPS header
  275.     fp.write("%!PS-Adobe-3.0 EPSF-3.0\n")
  276.     fp.write("%%Creator: PIL 0.1 EpsEncode\n")
  277.         #fp.write("%%CreationDate: %s"...)
  278.     fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size)
  279.     fp.write("%%Pages: 1\n")
  280.     fp.write("%%EndComments\n")
  281.     fp.write("%%Page: 1 1\n")
  282.     fp.write("%%ImageData: %d %d " % im.size)
  283.     fp.write("%d %d 0 1 1 \"%s\"\n" % operator)
  284.  
  285.     #
  286.     # image header
  287.     fp.write("gsave\n")
  288.     fp.write("10 dict begin\n")
  289.     fp.write("/buf %d string def\n" % (im.size[0] * operator[1]))
  290.     fp.write("%d %d scale\n" % im.size)
  291.     fp.write("%d %d 8\n" % im.size) # <= bits
  292.     fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1]))
  293.     fp.write("{ currentfile buf readhexstring pop } bind\n")
  294.     fp.write("%s\n" % operator[2])
  295.  
  296.     ImageFile._save(im, fp, [("eps", (0,0)+im.size, 0, None)])
  297.  
  298.     fp.write("\n%%%%EndBinary\n")
  299.     fp.write("grestore end\n")
  300.     fp.flush()
  301.  
  302. #
  303. # --------------------------------------------------------------------
  304.  
  305. Image.register_open(EpsImageFile.format, EpsImageFile, _accept)
  306.  
  307. Image.register_save(EpsImageFile.format, _save)
  308.  
  309. Image.register_extension(EpsImageFile.format, ".ps")
  310. Image.register_extension(EpsImageFile.format, ".eps")
  311.  
  312. Image.register_mime(EpsImageFile.format, "application/postscript")
  313.